/**
 * VERSIONING:
 * v1.0 - Created validation.js
 * v1.1 - Added method to check keys of date and time inputs + bugfixes
 * v1.2 - Added decimal validaiton
 * v1.3 - Fixed number regex
 * v1.4 - Changed validadtion style so that only one validate form method is needed
 *        + add text validation
 * v1.5 - Add select validation
 * v1.6 - Added data-required attribute check
 * v1.7 - Added id and integer validation checks
 * v1.8 - Add data-maxlength attribute check
 * v1.9 - Add check for hidden fields and dont validate them
 * v2.0 - Fix date regex
 * v2.1 - Update date and time key checks to be much more strict
 * v2.2 - Added remove error method
 * v2.3 - Update time regex to allow two formats HH:MM:SS and HH:MM
 */
var validation = UTSDK.VALIDATION;
/**
 * Validates the specified form using the data-validation attribute to decipher 
 * which validation to call.  If the element is hidden or has its data-required 
 * attribute set to false then it is skipped.  If the data-maxlength attribute 
 * is set then its length is checked.  If the data-match attribute is specified 
 * the value of the element is check against the element matching the id of the
 * attribute.
 * @param {type} id of form to validate
 * @returns {Boolean} returns true if form is valid
 */
validation.validate = function(id) {
    var form = UTSDK.$(id);
    var validated = true;
    var current;
    for (var i = 0; i < form.elements.length - 1; i++) {
        current = form.elements[i];
        if (UTSDK.isHidden(current))
            continue;
        var maxLength = parseInt(current.getAttribute("data-maxlength"));
        if (maxLength) {
            if (current.value.length > maxLength) {
                validation.checkInput(current.id, "", "Input to long. Max length is " + maxLength + ".", true);
                validated = false;
                continue;
            }
        }
        var required = current.getAttribute("data-required");
        if (required) {
            if ((current.value === "" || current.value === "null" ||
                    current.tagName === "SELECT") && required === "false") {
                validation.removeError(current.id);
                continue;
            }
        }
        var validationType = current.getAttribute("data-validation");
        if (validationType)
            if (validation[validationType](current.id))
                validated = false;
        var validationMatch = current.getAttribute("data-match");
        if (validationMatch) {
            if (current.value !== UTSDK.$(validationMatch).value) {
                validation.checkInput(current.id, "", current.getAttribute("data-matchmessage"), true);
                validated = false;
            }
        }
    }
    return validated;
};
/**
 * Takes in an elements id and attempts to match it to the specified regex.  
 * If the format is null or empty it is automatically invalidated.
 * If invalidated sets the border and error message to red.  Other wise default border colour
 * @param {id} id the id of the element being evaluated
 * @param {regex} format the regex being evaluted
 * @param {string} errorMessage the default validation message placeholder
 * @param {boolean} override whether to override the specified validationmessage
 * @returns {Boolean} returns true if invalid
 */
validation.checkInput = function(id, format, errorMessage, override) {
    var input = UTSDK.$(id);
    var message = input.getAttribute("data-validationmessage");
    if (!message || override) {
        message = errorMessage;
    }
    var error = UTSDK.$(id + "Error");
    if (format === null || format === "") {
        error.innerHTML = message;
        error.style.color = "red";
        UTSDK.addClass(input, "error");
        return true;
    }
    else if (input.value.match(format)) {
        error.innerHTML = "";
        UTSDK.removeClass(input, "error");
        return false;
    }
    else {
        error.innerHTML = message;
        error.style.color = "red";
        UTSDK.addClass(input, "error");
        return true;
    }
};
/**
 * Removes error message and red border
 * @param {id} id of element to remove error from
 */
validation.removeError = function(id) {
    var input = UTSDK.$(id);
    var error = UTSDK.$(id + "Error");
    error.innerHTML = "";
    UTSDK.removeClass(input, "error");
};
/**
 * Checks if the text field in empty
 * @param {type} id the element being evaluated
 * @returns {Boolean} True if invalid
 */
validation.text = function(id) {
    return validation.isEmpty(id);
};
validation.isEmpty = function(id) {
    return validation.checkInput(id, ".+", "Must not be empty");
};
/**
 * Checks if the select has the value of "none", returns true if it has
 * @param {type} id of the element to be evaluated
 * @returns {Boolean} returns true if value is none
 */
validation.select = function(id) {
    var value = UTSDK.$(id).value;
    if (value === "null" || value === "none")
        return validation.checkInput(id, "", "Not in database");
    else
        return false;
};
/**
 * Checks an input value against a regular expression
 * return true if the input is invalid
 * @param {id} id of element being evaluated
 * @returns {Boolean} true if invalid
 */
validation.email = function(id) {
    var format = "[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}";
    return validation.checkInput(id, format, "Not a valid email");
};
validation.password = function(id) {
    var format = "(((?=^[A-za-z]+.*)(?=.*[A-Z]+.*)(?=.*[a-z]+.*)(?=.*[0-9]+.*)).{8,})";
    return validation.checkInput(id, format, "Invalid password");
};
validation.number = function(id) {
    var format = /^(?:(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[\ \\\/]?)?((?:\(?\d{1,}\)?[\ \\\/]?){0,})?$/i;
    return validation.checkInput(id, format, "Not a valid contact number");
};
validation.name = function(id) {
    var format = "^[A-Za-z-']+$";
    return validation.checkInput(id, format, "Must only contain alphabetic, apostrophe or hyphen characters");
};
validation.id = function(id) {
    var format = "^[0-9]{8}$";
    return validation.checkInput(id, format, "Not a valid ID");
};
validation.integer = function(id) {
    var format = "^[0-9]$";
    return validation.checkInput(id, format, "Must be an integer");
};
validation.date = function(id) {
    var format = "^([0-9]{4})-([0-9]{2})-([0-9]{2})$";
    var after = UTSDK.$(id).getAttribute("data-after");
    if (after) {
        if (!validation.dateAfter(id, after))
            return validation.checkInput(id, "", "End date must be after start date", true);
    }
    return validation.checkInput(id, format, "Incorrect date format");
};
validation.time = function(id) {
    var format = "^(([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9])|(([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])$";
    var after = UTSDK.$(id).getAttribute("data-after");
    if (after) {
        if (!validation.timeAfter(id, after))
            return validation.checkInput(id, "", "End time must be after start time", true);
    }
    return validation.checkInput(id, format, "Incorrect time format");
};
validation.decimal = function(id) {
    var elem = UTSDK.$(id);
    var places = elem.getAttribute("data-dp");
    if (places == null)
        places = "";
    var format = "^[0-9]+((\.[0-9]{1," + places + "})){0,1}$";
    return validation.checkInput(id, format, "Incorrect decimal format");
};
/**
 * Returns whether one data is before another when dates are of the format
 * YYY-MM-DD and parent element id are specified
 * @param {string} id current element id
 * @param {string} after other element id
 * @returns {Boolean} true if element id is after element after
 */
validation.dateAfter = function(id, after) {
    var start = UTSDK.$(after).value.split("-");
    var end = UTSDK.$(id).value.split("-");
    for (var i = 0; i < start.length; i++) {
        if (start > end) {
            return false;
        }
    }
    return true;
};
/**
 * Returns whether one time is before another when times are of the format
 * HH-MM and parent element id are specified
 * @param {string} id current element id
 * @param {string} after other element id
 * @returns {Boolean} true if element id is after element after
 */
validation.timeAfter = function(id, after) {
    var start = UTSDK.$(after).value.split(":");
    var end = UTSDK.$(id).value.split(":");
    if (start[0] > end[0]) {
        return false;
    }
    else if (start[0] === end[0]) {
        if (start[1] > end[1])
            return false;
    }
    return true;
};
/**
 * Checks whether the input is valid i.e. only allowed characters.
 * Allowed characters include numbers, system keys and a hyphen.
 * If the entered value falls outside the valid range it is automatically updated
 * @param {event} evt the keydown event
 */
validation.checkDateKeys = function(evt) {
    var keyCode = evt.keyCode + "";
    if (constants.systemKeys["Backspace"] === keyCode) {
        //Only use backspace at the end of the input for validation reasons
        var position = evt.target.value.slice(0, evt.target.selectionStart).length;
        if (position !== evt.target.value.length) {
            evt.preventDefault();
            return false;
        }
    }
    if (evt.target.value.length === 4) { //check year
        var year = new Date().getFullYear();
        if (evt.target.value > year + 3) {
            evt.target.value = year + 3;
            validation.printError(evt.target.id, "Max year is " + (year + 3));
        }
        if (evt.target.value < year) {
            evt.target.value = year;
            validation.printError(evt.target.id, "Minimum year is " + year);
        }
    }
    if (evt.target.value.length === 7) { //check month
        var month = new Date().getMonth() + 1;
        var inputyear = evt.target.value.substring(0, 4);
        var inputmonth = evt.target.value.substring(5, 7);
        if (inputyear === "2014" && inputmonth < month) {
            evt.target.value = inputyear + "-" + month;
            validation.printError(evt.target.id, "Month has already past");
        }
        else if (inputmonth > 12) {
            evt.target.value = inputyear + "-12";
            validation.printError(evt.target.id, "Max month is 12");
        }
    }
    if (evt.target.value.length === 9) { //check day
        var inputyear = evt.target.value.substring(0, 4);
        var inputmonth = evt.target.value.substring(5, 7);
        var inputday = evt.target.value.substring(8, 9) + UTSDK.reverseJson(constants.numbers)[keyCode];
        var maxDays = new Date(inputyear, inputmonth, 0).getDate();
        if (inputday > maxDays) {
            evt.target.value = inputyear + "-" + inputmonth + "-" + maxDays;
            validation.printError(evt.target.id, "Max days is " + maxDays);
        }
        if (inputday <= 0) {
            evt.target.value = inputyear + "-" + inputmonth + "-01";
            validation.printError(evt.target.id, "Min days is 1");
        }
    }
    if (evt.target.value.length === 4 ||
            evt.target.value.length === 7) {  //Only allow a hyphen at the 4th and 7th positions
        for (var key in constants.systemKeys)
            if (constants.systemKeys[key] === keyCode)
                return true;
        if (keyCode === "189" //chrome, ie and safari hyphen
                || keyCode === "173") //firefox colon
            return true;
        evt.preventDefault();
        return false;
    }
    if (evt.target.value.length === 10) {  //Only allow system keys when date is filled
        for (var key in constants.systemKeys)
            if (constants.systemKeys[key] === keyCode)
                return true;
        evt.preventDefault();
        return false;
    }
    for (var key in constants.numbers)  //check if entered key is a number without shift
        if (constants.numbers[key] === keyCode && !evt.shiftKey)
            return true;
    for (var key in constants.systemKeys) //check if the entered key is a system key
        if (constants.systemKeys[key] === keyCode)
            return true;
    evt.preventDefault(); //All other keys are not propagated
    return false;
};
/**
 * Checks whether the input is valid i.e. only allowed characters.
 * Allowed characters include numbers, system keys and a colon.
 * If the entered value falls outside the valid range it is automatically updated
 * @param {event} evt the keydown event
 */
validation.checkTimeKeys = function(evt) {
    var keyCode = evt.keyCode + "";
    if (constants.systemKeys["Backspace"] === keyCode) {
        //Only use backspace at the end of the input for validation reasons
        var position = evt.target.value.slice(0, evt.target.selectionStart).length;
        console.log(position + " " + evt.target.value.length);
        if (position !== evt.target.value.length) {
            evt.preventDefault();
            return false;
        }
    }
    if (evt.target.value.length === 2) { //check hour
        var hour = evt.target.value.substring(0, 2);
        if (hour > 23) {
            validation.printError(evt.target.id, "Time range is 00:00 - 23:59");
            evt.target.value = "23";
        }
    }
    if (evt.target.value.length === 4) { //check minute
        var hour = evt.target.value.substring(0, 2);
        var time = evt.target.value.substring(3, 4) + UTSDK.reverseJson(constants.numbers)[keyCode];
        if (time > 59) {
            validation.printError(evt.target.id, "Time range is 00:00 - 23:59");
            evt.target.value = hour + ":59";
        }
    }
    if (evt.target.value.length === 2) { //In the 3rd position only allow colon
        for (var key in constants.systemKeys)
            if (constants.systemKeys[key] === keyCode)
                return true;
        if (keyCode === "186" //chrome, ie and safari colon
                || keyCode === "59") //firefox and opera colon
            return true;
        evt.preventDefault();
        return false;
    }
    if (evt.target.value.length === 5) {  //only allow system keys at max length
        for (var key in constants.systemKeys)
            if (constants.systemKeys[key] === keyCode)
                return true;
        evt.preventDefault();
        return false;
    }

    for (var key in constants.numbers) //Check numbers without shift
        if (constants.numbers[key] === keyCode && !evt.shiftKey)
            return true;
    for (var key in constants.systemKeys) //check system keys
        if (constants.systemKeys[key] === keyCode)
            return true;
    evt.preventDefault();
    return false;
};
/**
 * Prints the specified error message to the screen in the error label location
 * @param {id} id the id of the element being validated
 * @param {string} message the error message to be displayed
 */
var timeout;
validation.printError = function(id, message) {
    var errorBox = UTSDK.$(id + "Error");
    errorBox.innerHTML = message;
    clearTimeout(timeout);
    timeout = setTimeout(function() {
        errorBox.innerHTML = "";
        errorBox.style.display = "block";
    }, 3000);
};